/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package fourplay.ai;

import fourplay.constants.Constants;
import fourplay.frontEnd.State;
import java.util.ArrayList;

/**
 * The artificial intellegent agent that uses alpha-beta pruning with min-max
 * in order to find the next efficent move.  Using node as the bases of trees
 * and with a heristic function that will be discussed later else.
 * @author CJ
 */
public class Ai {
    public static int getMove(State state){
        return 0;
    }

    public static int getRandomMove(State state){
       int col;
       do{
           col = (int)(Math.random() * Constants.NUM_COL);
       }while(state.addComputer(col) == -1);
       return col;
    }

    public static int getHeristicMove(State state, int depth, char myChar){
        if(Constants.COMP_SYM != myChar && Constants.PLAY_SYM != myChar)
            throw new IllegalArgumentException("Invalid my Char");
        /*Max max = new Max();
        Min min = new Min();
        min.value = 6969;
        max.value = -6969;*/
        return maxMove(new Node(state, 0, -1), -696969, 696969, depth, myChar).getMove();
    }

    protected static Node maxMove(Node node, int max, int min, int maxDepth, char myChar){
        if(node.getState().getWinner() != Constants.EMPTY ||
                node.getDepth() >= maxDepth){
            node.setValue(Ai.heristic(node.state, myChar));
            return node;
        }
        Node[] successors = Ai.getSuccessors(node, myChar);
        Node maxNode = null;
        int maxValue = -696969;
        for(int i = 0; i < successors.length; ++i){
            Node current = successors[i];
            current.setValue(minMove(current, max, min, maxDepth, myChar).getValue());
            if(current.getValue() > maxValue){
                maxValue = current.getValue();
                maxNode = current;
            }
            if(current.getValue() > min){
                return current;
            }
            //Housekeeping for Max
            if(current.getValue() > max)
                max = current.getValue();
        }
        return maxNode;
    }

    protected static Node minMove(Node node, int max, int min, int maxDepth, char myChar){
        if(node.getState().getWinner() != Constants.EMPTY ||
                node.getDepth() >= maxDepth){
            node.setValue(Ai.heristic(node.state, myChar));
            return node;
        }
        char enemiesChar = (myChar == Constants.COMP_SYM)? Constants.PLAY_SYM: Constants.COMP_SYM;
        Node[] successors = Ai.getSuccessors(node, enemiesChar);
        int minValue = 696969;
        Node minNode = null;
        for(int i = 0 ; i < successors.length; ++i){
            Node current = successors[i];
            current.setValue(maxMove(current, max, min, maxDepth, myChar).getValue());
            if(current.getValue() < minValue){
                minValue = current.getValue();
                minNode = current;
            }
            if(current.getValue() < max){
                return current;
            }
            //HouseKeeping for Min
            if(current.getValue() < min){
                min = current.getValue();
            }
        }
        return minNode;

    }

    public static Node[] getSuccessors(Node node, char myChar){
        ArrayList<Node> temp = new ArrayList<Node>();
        State currentState = node.getState();
        for(int i = 0; i < Constants.NUM_COL; ++i){
            State nextState = currentState.clone();
            if(nextState.addToCol(i, myChar) != -1)
                temp.add(new Node(nextState, node.getDepth() + 1, i));
        }

        Node[] result = new Node[temp.size()];
        for(int i = 0; i < temp.size(); ++i)
            result[i] = temp.get(i);

        return result;
    }

    /**
     * This will be a basic layout of what I want to acheive below.
     * Step one:
     * Find if there was a winner, if there was, make the value pretty much max
     * or min.
     *
     * Step Two:
     * Find all the potential connections that myChar can have, weight each one
     * individually.
     * 3 currently connected with potentail win
     * 2 currently connected with potential win
     * 1 currently connected with potentail win
     *
     * Step Three:
     * Find all the potentail connections that enemyChar can have, mirror of a
     * above.
     *
     * Return Result:Step Two - Result: Step Three || Result:Step One
     * @param state, State to pass judgement upon
     * @param myChar, Who am I in life?
     * @return The verdict.
     */
    protected static int heristic(State state, char myChar){

        char[][] board = state.getBoard();

        char enyChar = (myChar == Constants.COMP_SYM)? Constants.PLAY_SYM: Constants.COMP_SYM;
        //Step One !!! Horray first one

        char potWinner = state.getWinner();
        if(potWinner == myChar)
            return 10000;
        else if(potWinner == enyChar){
            return -10000;
        }
        //Wow that actaully works...

        //Step Two Hard part one

        int result = 0;

        //Test Vertical for AI
        int[] vertical = vertical(board, myChar);
        //System.out.println(state);
        //System.out.println("Vertical Testing: Num 1: " + vertical[0] + " | Num 2: " + vertical[1] +
          //      " | Num 3: " + vertical[2]);

        result += vertical[0] * Constants.WEIGHT_AI_ONE;
        result += vertical[1] * Constants.WEIGHT_AI_TWO;
        result += vertical[2] * Constants.WEIGHT_AI_THREE;

        //Repeat for Enemy Vertical
        vertical = vertical(board, enyChar);

        result += vertical[0] * Constants.WEIGHT_PLY_ONE;
        result += vertical[1] * Constants.WEIGHT_PLY_TWO;
        result += vertical[2] * Constants.WEIGHT_PLY_THREE;

        //Test Horizontal for AI
        int[] horizontal = horizontal(board, myChar);
        
        //System.out.println(state);
        //System.out.println("Horizontal Test: Num 1: " + horizontal[0] + " | Num 2: " + horizontal[1] +
                //  " | Num 3: " + horizontal[2]);

        result += horizontal[0] * Constants.WEIGHT_AI_ONE;
        result += horizontal[1] * Constants.WEIGHT_AI_TWO;
        result += horizontal[2] * Constants.WEIGHT_AI_THREE;

        //Repeat for Enemy Horizontal
        horizontal = horizontal(board, enyChar);

        result += horizontal[0] * Constants.WEIGHT_PLY_ONE;
        result += horizontal[1] * Constants.WEIGHT_PLY_TWO;
        result += horizontal[2] * Constants.WEIGHT_PLY_THREE;


        //Test DownDiagnol for AI
        int[] downDiagnol = downDiagnol(board, myChar);
        //System.out.println(state);
        //System.out.println("Down Diagnol Test: Num 1: " + downDiagnol[0] + " | Num 2: " + downDiagnol[1] +
            //      " | Num 3: " + downDiagnol[2]);

        result += downDiagnol[0] * Constants.WEIGHT_AI_ONE;
        result += downDiagnol[1] * Constants.WEIGHT_AI_TWO;
        result += downDiagnol[2] * Constants.WEIGHT_AI_THREE;

        //Repeat for Enemy DownDiagnol
        downDiagnol = downDiagnol(board, enyChar);

        result += downDiagnol[0] * Constants.WEIGHT_PLY_ONE;
        result += downDiagnol[1] * Constants.WEIGHT_PLY_TWO;
        result += downDiagnol[2] * Constants.WEIGHT_PLY_THREE;

        //Test UpDiagnol for AI
        int[] upDiagnol = upDiagnol(board, myChar);
        //System.out.println(state);
        //System.out.println("Down Diagnol Test: Num 1: " + downDiagnol[0] + " | Num 2: " + downDiagnol[1] +
            //      " | Num 3: " + downDiagnol[2]);

        result += upDiagnol[0] * Constants.WEIGHT_AI_ONE;
        result += upDiagnol[1] * Constants.WEIGHT_AI_TWO;
        result += upDiagnol[2] * Constants.WEIGHT_AI_THREE;

        //Repeat for Enemy DownDiagnol
        upDiagnol = upDiagnol(board, enyChar);

        result += upDiagnol[0] * Constants.WEIGHT_PLY_ONE;
        result += upDiagnol[1] * Constants.WEIGHT_PLY_TWO;
        result += upDiagnol[2] * Constants.WEIGHT_PLY_THREE;



        return result;
    }

    /**
     * Helper Function for Heristic.
     * >.> less rewrites of code
     * @param state
     * @param myChar
     * @return Number of UNBLOCKED verticals, int[0] = length 1, int[1] = 2, int[2]=3.
     */
    protected static int[] vertical(char[][] board, char myChar){
        int[] result = new int[3];
        for(int j = 0; j < board[0].length; ++j){
            for(int i = board.length - 1; i > 2; --i){
                //Populate a block
                int[] current = new int[4];
                for(int k = 0; k < 4; ++k){
                    current[k] = board[i-k][j];
                }

                int count=-1;
                //Now check the block
                for(int k = 0; k < current.length; ++k){
                    if(current[k]!= myChar && current[k]!= Constants.EMPTY){
                        count=-2;
                        break;
                    }else if(current[k]==myChar)
                        count++;
                }
                if(count > -1){
                    result[count]++;
                }
            }
        }
        return result;
    }

    /**
     * Another helper function for heristic.  This is for horizontal though,
     * should be alot harder... I think it will be.
     *
     * @param state
     * @param myChar
     * @return Number of UNBLOCKED horizontals, same format as vertical
     */
    public static int[] horizontal(char[][] board, char myChar){
        int[] result = new int[3];
        for(int i = 0; i < board.length; ++i){
            for(int j = 0; j < board[i].length - 3; ++j){
                //Populate a block
                int[] current = new int[4];
                for(int k = 0; k < 4; ++k){
                    current[k] = board[i][j+k];
                }

                //Evaluate the block
                int count = -1;
                for(int k = 0; k < current.length; ++k){
                    if(current[k]!= myChar && current[k]!= Constants.EMPTY){
                        count=-2;
                        break;
                    }else if(current[k]==myChar)
                        count++;
                }
                if(count > -1){
                    result[count]++;
                }
            }
        }
        return result;
    }

    /**
     * Again, another helper fuctions.  It gets easier!
     * @param board
     * @param myChar
     * @return Number of UNBLOCKED downDiagnol, same format as vertical
     */
    public static int[] downDiagnol(char[][] board, char myChar){
        int[] result = new int[3];
        for(int i = 0; i < board.length - 3; ++i){
            for(int j = 0; j < board[i].length - 3; ++j){
                //Populate a Block
                int[] current = new int[4];
                for(int k = 0; k < 4; ++k){
                    current[k] = board[i + k][j + k];
                }

                //Evalute the Block
                int count = -1;
                for(int k = 0; k < current.length; ++k){
                    if(current[k]!= myChar && current[k]!= Constants.EMPTY){
                        count=-2;
                        break;
                    }else if(current[k]==myChar)
                        count++;
                }
                if(count > -1){
                    result[count]++;
                }
            }
        }

        return result;
    }

    /**
     * Another function to help out the heristic
     * @param board
     * @param myChar
     * @return Return the UNBLOCKED number, format as vertical
     */
    public static int[] upDiagnol(char[][] board, char myChar){
        int[] result = new int[3];
        for(int i = board.length - 1; i > 2; --i){
            for(int j = 0; j < board[i].length - 3; ++j){
                //Populate the Block
                int[] current = new int[4];
                for(int k = 0; k < 4; ++k){
                    current[k] = board[i-k][j+k];
                }

                //Evalute the BLock
                int count = -1;
                for(int k = 0; k < current.length; ++k){
                    if(current[k]!= myChar && current[k]!= Constants.EMPTY){
                        count=-2;
                        break;
                    }else if(current[k]==myChar)
                        count++;
                }
                if(count > -1){
                    result[count]++;
                }
            }
        }
        return result;
    }
}
